home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.sources.misc
- From: wietse@wzv.win.tue.nl (Wietse Venema)
- Subject: v26i095: unproto - compile ANSI C with old C compiler, Part02/02
- Message-ID: <1991Dec1.143532.21763@sparky.imd.sterling.com>
- X-Md4-Signature: e41270a11de3ed19fd262a273924fc94
- Date: Sun, 1 Dec 1991 14:35:32 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: wietse@wzv.win.tue.nl (Wietse Venema)
- Posting-number: Volume 26, Issue 95
- Archive-name: unproto/part02
- Environment: SYSV2, SunOS
- Supersedes: unproto: Volume 23, Issue 12-13
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 2 (of 2)."
- # Contents: unproto.c
- # Wrapped by wietse@wzv on Sun Dec 1 12:43:04 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f unproto.c -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"unproto.c\"
- else
- echo shar: Extracting \"unproto.c\" \(19406 characters\)
- sed "s/^X//" >unproto.c <<'END_OF_unproto.c'
- X/*++
- X/* NAME
- X/* unproto 1
- X/* SUMMARY
- X/* ANSI C to old C converter
- X/* PACKAGE
- X/* unproto
- X/* SYNOPSIS
- X/* /lib/cpp ... | unproto
- X/*
- X/* /somewhere/cpp ...
- X/* DESCRIPTION
- X/* This document describes a filter that sits between the
- X/* C preprocessor (usually \fI/lib/cpp\fP) and the next C compiler
- X/* pass. It rewrites ANSI-C style function headers, function type
- X/* declarations, function pointer types, and function pointer casts
- X/* to old style. Other ANSI-isms are passed on without modification
- X/* (token pasting, pragmas, etcetera).
- X/*
- X/* For maximal flexibility, the "cpp | unproto" pipeline can be
- X/* packaged as an executable shell script named "/somewhere/cpp".
- X/* This script should then be specified to the C compiler as a
- X/* non-default preprocessor. It will not work if your C compiler
- X/* specifies output file names to the preprocessor.
- X/*
- X/* The overhead of shell script interpretation can be avoided by
- X/* having the unprototyper itself open the pipe to the preprocessor.
- X/* In that case, the source should be compiled with the PIPE_THROUGH_CPP
- X/* macro defined (usually as "/lib/cpp"), and the resulting binary
- X/* should be installed as "/somewhere/cpp".
- X/* SEE ALSO
- X/* .ad
- X/* .fi
- X/* cc(1), how to specify a non-default C preprocessor.
- X/*
- X/* Some versions of the lint command are implemented as a shell
- X/* script. It should require only minor modification for integration
- X/* with the unprotoizer. Other versions of the lint command accept the same
- X/* command syntax as the C compiler for the specification of a non-default
- X/* preprocessor. Some research may be needed.
- X/* DIAGNOSTICS
- X/* The progam will complain if it unexpectedly
- X/* reaches the end of input.
- X/* BUGS
- X/* Should be run on preprocessed source only, i.e. after macro expansion.
- X/*
- X/* Declarations of (whatever) are misunderstood and will result in
- X/* syntax errors.
- X/*
- X/* Does not generate explicit type casts for function argument
- X/* expressions.
- X/* AUTHOR(S)
- X/* Wietse Venema (wietse@wzv.win.tue.nl)
- X/* Eindhoven University of Technology
- X/* Department of Mathematics and Computer Science
- X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
- X/* LAST MODIFICATION
- X/* 91/09/22 21:21:35
- X/* VERSION/RELEASE
- X/* 1.2
- X/*--*/
- X
- Xstatic char unproto_sccsid[] = "@(#) unproto.c 1.3 91/11/30 21:10:30";
- X
- X/* C library */
- X
- X#include <stdio.h>
- X#include <errno.h>
- X
- Xextern void exit();
- Xextern int optind;
- Xextern char *optarg;
- Xextern int getopt();
- X
- X/* Application-specific stuff */
- X
- X#include "vstring.h"
- X#include "stdarg.h"
- X#include "token.h"
- X#include "error.h"
- X#include "symbol.h"
- X
- X/* Forward declarations. */
- X
- Xstatic struct token *dcl_flush();
- Xstatic void block_flush();
- Xstatic void block_dcls();
- Xstatic struct token *show_func_ptr_type();
- Xstatic struct token *show_struct_type();
- Xstatic void show_arg_name();
- Xstatic void show_type();
- Xstatic void pair_flush();
- Xstatic void check_cast();
- X
- X#define check_cast_flush(t) (check_cast(t), tok_free(t))
- X
- X#ifdef PIPE_THROUGH_CPP
- Xstatic int pipe_stdin_through_cpp();
- X#endif
- X
- X/* Disable debugging printfs while preserving side effects. */
- X
- X#ifdef DEBUG
- X#define DPRINTF printf
- X#else
- X#define DPRINTF (void)
- X#endif
- X
- X/* An attempt to make some complicated expressions a bit more readable. */
- X
- X#define STREQ(x,y) (*(x) == *(y) && !strcmp((x),(y)))
- X
- X#define LAST_ARG_AND_EQUAL(s,c) ((s)->next == 0 && (s)->head \
- X && ((s)->head == (s)->tail) \
- X && (STREQ((s)->head->vstr->str, (c))))
- X
- X#define LIST_BEGINS_WITH_STAR(s) (s->head->head && s->head->head->tokno == '*')
- X
- X#define IS_FUNC_PTR_TYPE(s) (s->tokno == TOK_LIST && s->next \
- X && s->next->tokno == TOK_LIST \
- X && LIST_BEGINS_WITH_STAR(s))
- X
- X/* main - driver */
- X
- Xint main(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X register struct token *t;
- X#ifdef PIPE_THROUGH_CPP /* pipe through /lib/cpp */
- X int cpp_status;
- X int wait_pid;
- X int cpp_pid;
- X
- X cpp_pid = pipe_stdin_through_cpp(argv);
- X#endif
- X
- X sym_init(); /* prime the symbol table */
- X
- X while (t = tok_class(DO_WSPACE)) {
- X if (t = dcl_flush(t)) { /* try declaration */
- X if (t->tokno == '{') { /* examine rejected token */
- X block_flush(t); /* body */
- X } else {
- X tok_flush(t); /* other, recover */
- X }
- X }
- X }
- X
- X#ifdef PIPE_THROUGH_CPP /* pipe through /lib/cpp */
- X while ((wait_pid = wait(&cpp_status)) != -1 && wait_pid != cpp_pid)
- X /* void */ ;
- X return (wait_pid != cpp_pid || cpp_status != 0);
- X#else
- X return (0);
- X#endif
- X}
- X
- X#ifdef PIPE_THROUGH_CPP /* pipe through /lib/cpp */
- X
- X/* pipe_stdin_through_cpp - avoid shell script overhead */
- X
- Xstatic int pipe_stdin_through_cpp(argv)
- Xchar **argv;
- X{
- X int pipefds[2];
- X int pid;
- X char **cpptr = argv;
- X
- X /*
- X * With most UNIX implementations, the second non-option argument to
- X * /lib/cpp specifies the output file. If an output file other than
- X * stdout is specified, we must force /lib/cpp to write to stdout, and we
- X * must redirect our own standard output to the specified output file.
- X */
- X
- X#define IS_OPTION(cp) ((cp)[0] == '-' && (cp)[1] != 0)
- X
- X /* Skip to first non-option argument, if any. */
- X
- X while (*++cpptr && IS_OPTION(*cpptr))
- X /* void */ ;
- X
- X /*
- X * Assume that the first non-option argument is the input file name. The
- X * next argument could be the output destination or an option (System V
- X * Release 2 /lib/cpp gets the options *after* the file arguments).
- X */
- X
- X if (*cpptr && *++cpptr && **cpptr != '-') {
- X
- X /*
- X * The first non-option argument is followed by another argument that
- X * is not an option ("-stuff") or a hyphen ("-"). Redirect our own
- X * standard output before we clobber the file name.
- X */
- X
- X if (freopen(*cpptr, "w", stdout) == 0) {
- X perror(*cpptr);
- X exit(1);
- X }
- X /* Clobber the file name argument so that /lib/cpp writes to stdout */
- X
- X *cpptr = "-";
- X }
- X /* Set up the pipe that connects /lib/cpp to our standard input. */
- X
- X if (pipe(pipefds)) {
- X perror("pipe");
- X exit(1);
- X }
- X switch (pid = fork()) {
- X case -1: /* error */
- X perror("fork");
- X exit(1);
- X case 0: /* child */
- X close(pipefds[0]); /* close reading end */
- X close(1); /* connect stdout to pipe */
- X if (dup(pipefds[1]) != 1)
- X error(1, "dup() problem");
- X close(pipefds[1]); /* close redundant fd */
- X execv(PIPE_THROUGH_CPP, argv);
- X perror(PIPE_THROUGH_CPP);
- X exit(1);
- X default: /* parent */
- X close(pipefds[1]); /* close writing end */
- X close(0); /* connect stdin to pipe */
- X if (dup(pipefds[0]) != 0)
- X error(1, "dup() problem");
- X close(pipefds[0]); /* close redundant fd */
- X return (pid);
- X }
- X}
- X
- X#endif
- X
- X/* header_flush - rewrite new-style function header to old style */
- X
- Xstatic void header_flush(t)
- Xregister struct token *t;
- X{
- X register struct token *s;
- X
- X /* Do argument names, but suppress void and rewrite trailing ... */
- X
- X if (LAST_ARG_AND_EQUAL(t->head, "void")) {
- X put_str("()\n"); /* no arguments */
- X } else {
- X for (s = t->head; s; s = s->next) { /* foreach argument... */
- X if (LAST_ARG_AND_EQUAL(s, "...")) {
- X#ifdef _VA_ALIST_ /* see ./stdarg.h */
- X put_ch(s->tokno); /* ',' */
- X put_str(_VA_ALIST_); /* varargs magic */
- X#endif
- X } else {
- X put_ch(s->tokno); /* opening '(' or ',' */
- X show_arg_name(s); /* extract argument name */
- X }
- X }
- X put_str(")\n"); /* closing ')' */
- X }
- X
- X /* Do argument types, but suppress void and trailing ... */
- X
- X if (!LAST_ARG_AND_EQUAL(t->head, "void")) {
- X for (s = t->head; s; s = s->next) { /* foreach argument... */
- X if (!LAST_ARG_AND_EQUAL(s, "...")) {
- X if (s->head != s->tail) { /* really new-style argument? */
- X show_line_control(); /* fix line number */
- X show_type(s); /* rewrite type info */
- X put_str(";\n");
- X }
- X }
- X }
- X }
- X tok_free(t);
- X show_line_control(); /* because '{' follows */
- X}
- X
- X/* show_arg_name - extract argument name from argument type info */
- X
- Xstatic void show_arg_name(s)
- Xregister struct token *s;
- X{
- X if (s->head) {
- X register struct token *p;
- X register struct token *t = 0;
- X
- X /* Find the last interesting item. */
- X
- X for (p = s->head; p; p = p->next) {
- X if (p->tokno == TOK_WORD) {
- X t = p; /* remember last word */
- X } else if (IS_FUNC_PTR_TYPE(p)) {
- X t = p; /* or function pointer */
- X p = p->next;
- X }
- X }
- X
- X /* Extract argument name from last interesting item. */
- X
- X if (t) {
- X if (t->tokno == TOK_LIST)
- X show_arg_name(t->head); /* function pointer, recurse */
- X else
- X tok_show(t); /* print last word */
- X }
- X }
- X}
- X
- X/* show_type - rewrite type to old-style syntax */
- X
- Xstatic void show_type(s)
- Xregister struct token *s;
- X{
- X register struct token *p;
- X
- X for (p = s->head; p; p = p->next) {
- X if (IS_FUNC_PTR_TYPE(p)) {
- X p = show_func_ptr_type(p); /* function pointer type */
- X } else {
- X tok_show(p); /* other */
- X }
- X }
- X}
- X
- X/* show_func_ptr_type - display function_pointer type using old-style syntax */
- X
- Xstatic struct token *show_func_ptr_type(t)
- Xstruct token *t;
- X{
- X register struct token *s;
- X
- X /*
- X * Rewrite (list1) (list2) to (list1) (). Only (list1) is given to us;
- X * the caller must have verified the presence of (list2). Account for the
- X * rare case that (list1) is a comma-separated list. That should be an
- X * error, but we do not want to waste any information.
- X */
- X
- X for (s = t->head; s; s = s->next) {
- X put_ch(s->tokno); /* opening paren or ',' */
- X show_type(s); /* recurse */
- X }
- X put_str(")()"); /* closing paren */
- X return (t->next);
- X}
- X
- X/* show_struct_type - display structured type, rewrite function-pointer types */
- X
- Xstatic struct token *show_struct_type(p)
- Xregister struct token *p;
- X{
- X tok_show(p); /* opening brace */
- X
- X while (p->next) { /* XXX cannot return 0 */
- X p = p->next;
- X if (IS_FUNC_PTR_TYPE(p)) {
- X p = show_func_ptr_type(p); /* function-pointer member */
- X } else if (p->tokno == '{') {
- X p = show_struct_type(p); /* recurse */
- X } else {
- X tok_show(p); /* other */
- X if (p->tokno == '}') {
- X return (p); /* done */
- X }
- X }
- X }
- X DPRINTF("/* missing '}' */");
- X return (p);
- X}
- X
- X/* is_func_ptr_cast - recognize function-pointer type cast */
- X
- Xstatic int is_func_ptr_cast(t)
- Xregister struct token *t;
- X{
- X register struct token *p;
- X
- X /*
- X * Examine superficial structure. Require (list1) (list2). Require that
- X * list1 begins with a star.
- X */
- X
- X if (!IS_FUNC_PTR_TYPE(t))
- X return (0);
- X
- X /*
- X * Make sure that there is no name in (list1). Do not worry about
- X * unexpected tokens, because the compiler will complain anyway.
- X */
- X
- X for (p = t->head->head; p; p = p->next) {
- X switch (p->tokno) {
- X case TOK_LIST: /* recurse */
- X return (is_func_ptr_cast(p));
- X case TOK_WORD: /* name in list */
- X return (0);
- X }
- X }
- X return (1); /* no name found */
- X}
- X
- X/* check_cast - display ()-delimited, comma-separated list */
- X
- Xstatic void check_cast(t)
- Xstruct token *t;
- X{
- X register struct token *s;
- X register struct token *p;
- X
- X /*
- X * Rewrite function-pointer types and function-pointer casts. Do not
- X * blindly rewrite (*list1)(list2) to (*list1)(). Function argument lists
- X * are about the only thing we can discard without provoking diagnostics
- X * from the compiler.
- X */
- X
- X for (s = t->head; s; s = s->next) {
- X put_ch(s->tokno); /* opening paren or ',' */
- X for (p = s->head; p; p = p->next) {
- X switch (p->tokno) {
- X case TOK_LIST:
- X if (is_func_ptr_cast(p)) { /* not: IS_FUNC_PTR_TYPE(p) */
- X p = show_func_ptr_type(p); /* or we might take away */
- X } else { /* function-call arguments */
- X check_cast(p); /* recurse */
- X }
- X break;
- X case '{':
- X p = show_struct_type(p); /* rewrite func. ptr. types */
- X break;
- X default:
- X tok_show(p);
- X break;
- X }
- X }
- X }
- X put_ch(')'); /* closing paren */
- X}
- X
- X/* block_dcls - on the fly rewrite decls/initializers at start of block */
- X
- Xstatic void block_dcls()
- X{
- X register struct token *t;
- X
- X /*
- X * Away from the top level, a declaration should be preceded by type or
- X * storage-class information. That is why inside blocks, structs and
- X * unions we insist on reading one word before passing the _next_ token
- X * to the dcl_flush() function.
- X *
- X * Struct and union declarations look the same everywhere: we make an
- X * exception for these more regular constructs and pass the "struct" and
- X * "union" tokens to the type_dcl() function.
- X */
- X
- X while (t = tok_class(DO_WSPACE)) {
- X switch (t->tokno) {
- X case TOK_WSPACE: /* preserve white space */
- X case '\n': /* preserve line count */
- X tok_flush(t);
- X break;
- X case TOK_WORD: /* type declarations? */
- X tok_flush(t); /* advance to next token */
- X t = tok_class(DO_WSPACE); /* null return is ok */
- X case TOK_COMPOSITE: /* struct or union */
- X if ((t = dcl_flush(t)) == 0)
- X break;
- X /* FALLTRHOUGH */
- X default: /* end of declarations */
- X DPRINTF("/* end dcls */");
- X /* FALLTRHOUGH */
- X case '}': /* end of block */
- X tok_unget(t);
- X return;
- X }
- X }
- X}
- X
- X/* block_flush - rewrite struct, union or statement block on the fly */
- X
- Xstatic void block_flush(t)
- Xregister struct token *t;
- X{
- X static int count = 0;
- X
- X tok_flush(t);
- X DPRINTF("/*%d*/", ++count);
- X
- X /*
- X * Rewrite function pointer types in declarations and function pointer
- X * casts in initializers at start of block.
- X */
- X
- X block_dcls();
- X
- X /* Remainder of block: only rewrite function pointer casts. */
- X
- X while (t = tok_class(DO_WSPACE)) {
- X if (t->tokno == TOK_LIST) {
- X check_cast_flush(t);
- X } else if (t->tokno == '{') {
- X block_flush(t);
- X } else {
- X tok_flush(t);
- X if (t->tokno == '}') {
- X DPRINTF("/*%d*/", count--);
- X return;
- X }
- X }
- X }
- X DPRINTF("/* missing '}' */");
- X}
- X
- X/* pair_flush - on the fly rewrite casts in grouped stuff */
- X
- Xstatic void pair_flush(t, start, stop)
- Xregister struct token *t;
- Xregister int start;
- Xregister int stop;
- X{
- X tok_flush(t);
- X
- X while (t = tok_class(DO_WSPACE)) {
- X if (t->tokno == start) { /* recurse */
- X pair_flush(t, start, stop);
- X } else if (t->tokno == TOK_LIST) { /* expression or cast */
- X check_cast_flush(t);
- X } else { /* other, copy */
- X tok_flush(t);
- X if (t->tokno == stop) { /* done */
- X return;
- X }
- X }
- X }
- X DPRINTF("/* missing '%c' */", stop);
- X}
- X
- X/* initializer - on the fly rewrite casts in initializer */
- X
- Xstatic void initializer()
- X{
- X register struct token *t;
- X
- X while (t = tok_class(DO_WSPACE)) {
- X switch (t->tokno) {
- X case ',': /* list separator */
- X case ';': /* list terminator */
- X tok_unget(t);
- X return;
- X case TOK_LIST: /* expression or cast */
- X check_cast_flush(t);
- X break;
- X case '[': /* array substript, may nest */
- X pair_flush(t, '[', ']');
- X break;
- X case '{': /* structured data, may nest */
- X pair_flush(t, '{', '}');
- X break;
- X default: /* other, just copy */
- X tok_flush(t);
- X break;
- X }
- X }
- X}
- X
- X/* func_ptr_dcl_flush - rewrite function pointer declaration */
- X
- Xstatic struct token *func_ptr_dcl_flush(list)
- Xregister struct token *list;
- X{
- X register struct token *t;
- X
- X /*
- X * Ignore blanks because they would be output earlier than the list that
- X * preceded them... Recover gracefully from syntax errors.
- X */
- X
- X while (t = tok_class(NO_WSPACE)) {
- X switch (t->tokno) {
- X case '\n': /* preserve line count */
- X tok_flush(t);
- X break;
- X case TOK_LIST:
- X /* Function pointer type: (list1) (list2) -> (list1) () */
- X (void) show_func_ptr_type(list); /* may be recursive */
- X tok_free(list);
- X tok_free(t);
- X return (0);
- X default: /* not a declaration */
- X tok_unget(t);
- X return (list);
- X }
- X }
- X
- X /* Hit EOF; must be mistake, but do not waste any information. */
- X
- X return (list);
- X}
- X
- X/* function_dcl_flush - rewrite function { heading, type declaration } */
- X
- Xstatic struct token *function_dcl_flush(list)
- Xregister struct token *list;
- X{
- X register struct token *t;
- X
- X /*
- X * Ignore blanks because they would be output earlier than the list that
- X * preceded them...
- X */
- X
- X while (t = tok_class(NO_WSPACE)) {
- X switch (t->tokno) {
- X case '\n':
- X /* Preserve line count */
- X tok_flush(t);
- X break;
- X case '{':
- X /* Function heading: word (list) { -> old style heading */
- X header_flush(list);
- X tok_unget(t);
- X return (0);
- X case TOK_WORD:
- X /* Old-style function heading: word (list) word...{ */
- X tok_flush(list);
- X tok_unget(t);
- X return (0);
- X case TOK_LIST:
- X /* Function typedef? word (list1) (list) -> word (list1) () */
- X tok_flush(list);
- X put_str("()");
- X tok_free(t);
- X return (0);
- X case ',':
- X case ';':
- X /* Function type declaration: word (list) -> word () */
- X tok_free(list);
- X put_str("()");
- X tok_unget(t);
- X return (0);
- X default:
- X /* Something else, reject the list. */
- X tok_unget(t);
- X return (list);
- X }
- X }
- X
- X /* Hit EOF; must be mistake, but do not waste any information. */
- X
- X return (list);
- X}
- X
- X/* dcl_flush - parse declaration on the fly, return rejected token */
- X
- Xstatic struct token *dcl_flush(t)
- Xregister struct token *t;
- X{
- X register int got_word;
- X
- X /*
- X * Away from the top level, type or storage-class information is required
- X * for an (extern or forward) function type declaration or a variable
- X * declaration.
- X *
- X * With our naive word-counting approach, this means that the caller should
- X * read one word before passing the next token to us. This is how we
- X * distinguish, for example, function declarations from function calls.
- X *
- X * An exception are structs and unions, because they look the same at any
- X * level. The caller should give is the "struct" or "union" token.
- X */
- X
- X for (got_word = 0; t; t = tok_class(DO_WSPACE)) {
- X switch (t->tokno) {
- X case TOK_WSPACE: /* advance past blanks */
- X case '\n': /* advance past newline */
- X case '*': /* indirection: keep trying */
- X tok_flush(t);
- X break;
- X case TOK_WORD: /* word: keep trying */
- X case TOK_COMPOSITE: /* struct or union */
- X got_word = 1;
- X tok_flush(t);
- X break;
- X default:
- X
- X /*
- X * Function pointer types can be preceded by zero or more words
- X * (at least one when not at the top level). Other stuff can be
- X * accepted only after we have seen at least one word (two words
- X * when not at the top level). See also the above comment on
- X * structs and unions.
- X */
- X
- X if (t->tokno == TOK_LIST && LIST_BEGINS_WITH_STAR(t)) {
- X if (t = func_ptr_dcl_flush(t)) {
- X return (t); /* reject token */
- X } else {
- X got_word = 1; /* for = and [ and , and ; */
- X }
- X } else if (got_word == 0) {
- X return (t); /* reject token */
- X } else {
- X switch (t->tokno) {
- X case TOK_LIST: /* function type */
- X if (t = function_dcl_flush(t))
- X return (t); /* reject token */
- X break;
- X case '[': /* dimension, does not nest */
- X pair_flush(t, '[', ']');
- X break;
- X case '=': /* initializer follows */
- X tok_flush(t);
- X initializer(); /* rewrite casts */
- X break;
- X case '{': /* struct, union, may nest */
- X block_flush(t); /* use code for stmt blocks */
- X break;
- X case ',': /* separator: keep trying */
- X got_word = 0;
- X tok_flush(t);
- X break;
- X case ';': /* terminator: succeed */
- X tok_flush(t);
- X return (0);
- X default: /* reject token */
- X return (t);
- X }
- X }
- X }
- X }
- X return (0); /* hit EOF */
- X}
- END_OF_unproto.c
- if test 19406 -ne `wc -c <unproto.c`; then
- echo shar: \"unproto.c\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- echo shar: End of archive 2 \(of 2\).
- cp /dev/null ark2isdone
- MISSING=""
- for I in 1 2 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked both archives.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-
- exit 0 # Just in case...
- --
- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM
- Sterling Software, IMD UUCP: uunet!sparky!kent
- Phone: (402) 291-8300 FAX: (402) 291-4362
- Please send comp.sources.misc-related mail to kent@uunet.uu.net.
-